package com.rw.tool.util.string;

import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 字符串扫描类
 */
public class StringScanner {
    // 输入的字符串
    private final String input;

    // 当前字符串扫描到的位置
    private int index = 0;

    // 数字类型的字符串正则表达式
    private static final Pattern FLOAT_PATTERN = Pattern.compile("^[\\-+]?\\d+(\\.\\d+)?");

    // 整数类型的字符串正则表达式
    private static final Pattern INTEGER_PATTERN = Pattern.compile("^[\\-+]?\\d+");

    // 数字字符类型的字符串正则表达式
    private static final Pattern NUMERIC_PATTERN = Pattern.compile("^\\d+");

    // 除了数字以外的正则表达式
    private static final Pattern NOT_INT_PATTERN = Pattern.compile("^\\D+");

    // 标识符类型的正则表达式
    private static final Pattern WORD_PATTERN = Pattern.compile("^\\w+");

    // 布尔类型的字符串正则表达式
    private static final Pattern BOOL_PATTERN = Pattern.compile("^(true|false)", Pattern.CASE_INSENSITIVE);

    public StringScanner(String input) {
        if (input == null) {
            throw new StringScannerException("Input string is null!");
        }
        this.input = input;
    }

    /**
     * 获取当前字符串扫描的位置
     */
    public int getIndex() {
        return index;
    }

    /**
     * 设置当前字符串扫描的位置
     */
    public void setIndex(int index) {
        if (index < 0 || index >= input.length()) {
            String errorMessage = "Index out of bounds! The index should be between 0 and {}";
            throw new IndexOutOfBoundsException(StringFormatter.format(errorMessage, input.length()));
        }
        this.index = index;
    }

    /**
     * 获取原字符串
     */
    public String getOriginalString() {
        return input;
    }

    /**
     * 字符串是否有剩余某一段未扫描
     */
    public boolean hasNext() {
        return index < input.length();
    }

    /**
     * 字符串接下来是否拥有匹配正则表达式的一段
     */
    public boolean hasNext(String regex) {
        Pattern pattern = Pattern.compile(addPrefix(regex));
        return originalHasNext(pattern);
    }

    /**
     * 字符串接下来是否有一段数字
     */
    public boolean hasNextNumber() {
        return originalHasNext(FLOAT_PATTERN);
    }

    /**
     * 字符串接下来是否有一段整数
     */
    public boolean hasNextInteger() {
        return originalHasNext(INTEGER_PATTERN);
    }

    /**
     * 字符串接下来是否有一段数字字符
     */
    public boolean hasNextNumeric() {
        return originalHasNext(NUMERIC_PATTERN);
    }

    /**
     * 字符串接下来是否有一段不含数字的字符串
     */
    public boolean hasNextNotInteger() {
        return originalHasNext(NOT_INT_PATTERN);
    }

    /**
     * 字符串接下来是否有一段标识符
     */
    public boolean hasNextWord() {
        return originalHasNext(WORD_PATTERN);
    }

    /**
     * 字符串接下来是否有一段布尔值
     */
    public boolean hasNextBoolean() {
        return originalHasNext(BOOL_PATTERN);
    }

    /**
     * 取出字符串接下来的所有字符
     */
    public String nextAll() {
        String next = input.substring(index);
        index = input.length();
        return next;
    }

    /**
     * 取出接下来匹配正则表达式的字符
     */
    public String next(String regex) {
        return originalNext(Pattern.compile(addPrefix(regex)));
    }

    /**
     * 取出接下来匹配的数字
     */
    public BigDecimal nextNumber() {
        return new BigDecimal(originalNext(FLOAT_PATTERN));
    }

    /**
     * 取出接下来匹配的数字字符
     */
    public String nextNumeric() {
        return originalNext(NUMERIC_PATTERN);
    }

    /**
     * 取出接下来一段不含数字的字符串
     */
    public String nextNotInteger() {
        return originalNext(NOT_INT_PATTERN);
    }

    /**
     * 取出接下来匹配的整数
     */
    public BigDecimal nextInteger() {
        return new BigDecimal(originalNext(INTEGER_PATTERN));
    }

    /**
     * 字符串接下来是否有一段标识符
     */
    public String nextWord() {
        return originalNext(WORD_PATTERN);
    }

    /**
     * 取出接下来匹配的布尔值
     */
    public boolean nextBoolean() {
        return Boolean.parseBoolean(originalNext(BOOL_PATTERN));
    }

    private String addPrefix(String regex) {
        if (StringUtil.startsWith(regex, "^")) {
            return regex;
        }
        return "^" + regex;
    }

    public boolean hasNext(Pattern pattern) {
        Pattern newPattern = Pattern.compile(addPrefix(pattern.pattern()), pattern.flags());
        return originalHasNext(newPattern);
    }

    public String next(Pattern pattern) {
        Pattern newPattern = Pattern.compile(addPrefix(pattern.pattern()), pattern.flags());
        return originalNext(newPattern);
    }

    private boolean originalHasNext(Pattern pattern) {
        Matcher matcher = pattern.matcher(input.substring(index));
        return matcher.find();
    }

    private String originalNext(Pattern pattern) {
        Matcher matcher = pattern.matcher(input.substring(index));
        if (!matcher.find()) {
            throw new StringScannerException("Illegal string match!");
        }
        String result = matcher.group();
        index += result.length();
        return result;
    }
}
